#include "Bloque.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <string.h>
using namespace std;

#define LONGITUD_MAXIMA 200
#define PROMEDIO_REGISTROS 30

const char carga[] = "-i";
const char listado[]= "-l";
const char clave[]= "-k";
const char ordClave[]= "-ok";
const char ordNombre[]= "-on";


//Crea un archivo binario con el nombre de la entidad
FILE* crearArchivoBinario(char *argv[]){

	char* nombre = argv[2];
	FILE *archivo = fopen(strcat(nombre,".dat"), "wb");
	return archivo;
}

//Abre un archivo binario con el nombre de la entidad
FILE* abrirArchivoBinario(char *argv[], int subindice){
	char *nombre = argv[subindice];
	FILE *archivo = fopen(strcat(nombre,".dat"), "rb");
	return archivo;
}

//Abre un archivo de texto con el nombre de la entidad
FILE* abrirArchivoDeTexto(char *argv[]){
	char *ruta_inicial = argv[3];
	char *nombre = strcat(strcat(ruta_inicial, "/"), argv[2]);
	FILE *archivo = fopen(strcat(nombre, ".txt"), "r");
	return archivo;
}

//Recorre los registro cargándolos en un bloque hasta llenarlo y almacenarlo en un archivo_binario
Registro* llenarBloque(FILE *archivo_de_texto, FILE *archivo_binario,Registro* registro_a_agregar){
	Bloque *bloque_auxiliar = new Bloque();
	//Bloque *puntero_a_bloque = &bloque_auxiliar;
	Registro *registro_auxiliar = NULL;
	char *nombre_registro;
	if(registro_a_agregar != NULL){
		bloque_auxiliar->agregarRegistro(registro_a_agregar);
	}
	while(!feof(archivo_de_texto)){
		int codigo;
		nombre_registro = (char*) malloc( (int) sizeof(char) * LONGITUD_MAXIMA);
		fscanf(archivo_de_texto, "%i,%[^\n]p", &codigo, nombre_registro);
		registro_auxiliar = new Registro(codigo, nombre_registro);
		if(!bloque_auxiliar->lleno(registro_auxiliar)){
			bloque_auxiliar->agregarRegistro(registro_auxiliar);
		}else{
			free (nombre_registro);
			fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_binario);
			delete bloque_auxiliar;
			return registro_auxiliar;
		}

		free (nombre_registro);
		delete registro_auxiliar;
	}
	fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_binario);
	return NULL;
}

//Corresponde a la instrucción -i
void cargarEntidades(char *argv[]){
	FILE *archivo_de_texto = abrirArchivoDeTexto(argv);
	FILE *archivo_binario = crearArchivoBinario(argv);
	Registro* reg = NULL;
	if((archivo_de_texto != NULL) && (archivo_binario != NULL)){
		cout << "Carga masiva de entidades:" << endl;
		cout << "La entidad es " << argv[2] << "\n" << endl;
		while(!feof(archivo_de_texto)){
			reg= llenarBloque(archivo_de_texto, archivo_binario,reg);
		}
		fclose(archivo_binario);
		fclose(archivo_de_texto);
	} else{
		printf("No se pudo abrir el archivo.");
	}
}

//Muestra por pantalla los registros de un archivo binario
char* mostrarDatos(char *puntero){
	int codigo = 0;
	memcpy(&codigo, puntero, 4);
	printf("%i,", codigo);
	puntero += 4;
	int longitud = 0;
	memcpy(&longitud, puntero, 4);
	puntero += 4;
	char *nombre = (char*) malloc( (int) sizeof(char) * (longitud + 1));
	memcpy(nombre, puntero, longitud);
	nombre[longitud] = '\0';
	puntero += longitud;
	printf("%s\n", nombre);
	delete[] nombre;
	return puntero;
}

//Recorre todos los registros para ir mostrándolos por pantalla
void listarEntidades(FILE *archivo){
	Bloque* bloque_auxiliar = new Bloque();
	//Bloque *puntero_a_bloque = &bloque_auxiliar;
	fread(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo);
	char *puntero_auxiliar = bloque_auxiliar->getBloque();
	int cantidad = 0;
	memcpy(&cantidad, puntero_auxiliar, 4);
	puntero_auxiliar += 4;
	for(int i = 0; i < cantidad; i++){
		puntero_auxiliar = mostrarDatos(puntero_auxiliar);
	}
	delete bloque_auxiliar;
}

//Corresponde a la instrucción -l
void listadoDeEntidades(char *argv[]){
	FILE *archivo = abrirArchivoBinario(argv, 2);
	if(archivo == NULL){
		printf("No se pudo abrir el archivo.");
	} else{
		cout << "Listado de entidades:" << endl;
		while(!feof(archivo)){
			listarEntidades(archivo);
		}
		fclose(archivo);
	}
}

//Se recorren los registros de un bloque buscando la clave
char* recorrerRegistros(char *puntero, int buscado, bool &encontrado){
	int codigo = 0;
	memcpy(&codigo, puntero, 4);
	if(codigo == buscado){
		puntero = mostrarDatos(puntero);
		encontrado = true;
	}
	else{
		puntero += 4;
		int longitud = 0;
		memcpy(&longitud, puntero, 4);
		puntero += (longitud + 4);
	}
	return puntero;
}

//Se busca la clave en un bloque
void busquedaEnElBloque(FILE *archivo, int buscado, bool &encontrado){
	Bloque bloque_auxiliar = Bloque();
	Bloque *puntero_a_bloque = &bloque_auxiliar;
	fread(puntero_a_bloque->getBloque(), TAMANIO_BLOQUE, 1, archivo);
	char *puntero_auxiliar = puntero_a_bloque->getBloque();
	int cantidad = 0;
	memcpy(&cantidad, puntero_auxiliar, 4);
	puntero_auxiliar += 4;
	int contador = 0;
	while((contador < cantidad) && !encontrado){
		puntero_auxiliar = recorrerRegistros(puntero_auxiliar, buscado, encontrado);
		contador++;
	}
}

//Corresponde a la instrucción -k
void buscarPorClave(char *argv[]){
	FILE *archivo = abrirArchivoBinario(argv, 3);
	if(archivo == NULL){
			printf("No se pudo abrir el archivo.");
	} else{
		cout << "Búsqueda por clave:" << endl;
		bool encontrado = false;
		int buscado = atoi(argv[2]);
		while((!encontrado) && (!feof(archivo))){
			busquedaEnElBloque(archivo, buscado, encontrado);
		}
		fclose(archivo);
		if(!encontrado){
			cout << "La entidad de código " << buscado << " no está registrada."<< endl;
		}
	}
}

//Devuelve la cantidad de dígitos de un entero
unsigned int contarDigitos(int numero){
	unsigned int contador = 1;
	while(numero /= 10){
		contador++;
	}
	return contador;
}



//Hidrata un registro que es pasado como un puntero a Char, y devuelve un puntero a Registro.
Registro* generarRegistro(char* &puntero){
	int codigo = 0;
	memcpy(&codigo, puntero, 4);
	puntero += 4;
	int longitud = 0;
	memcpy(&longitud, puntero, 4);
	puntero += 4;
	char *nombre = (char*) malloc( sizeof(char) * (longitud+1));
	memcpy(nombre, puntero, longitud);
	nombre[longitud] = '\0';
	puntero += longitud;
	Registro* registro_auxiliar = new Registro(codigo, nombre);
	free(nombre);
	//puntero += 1;
	//Registro *puntero_a_registro = &registro_auxiliar;
	return registro_auxiliar;
}

//Intercambia de posición dos registros dentro de un buffer[Array de punteros a Registros] que se pasa por referencia.
void intercambiarRegistros(Registro *buffer[], int posicion_registro_sube, int posicion_registro_baja){
	Registro* registro_auxiliar = buffer[posicion_registro_sube];
	buffer[posicion_registro_sube] = buffer[posicion_registro_baja];
	buffer[posicion_registro_baja] = registro_auxiliar;
}


//Reestrablece el Heap, luego de realizar una inserción.
void reestablecerHeapPorInsercionCodigo(Registro *buffer[], int posicion_registro_nuevo){
	int codigo_registro_nuevo;
	int codigo_registro_padre;
	int posicion_registro_padre;
	bool heap_reestablecido = false;

	while (!heap_reestablecido){
		posicion_registro_padre = (div(posicion_registro_nuevo - 1, 2)).quot;
		codigo_registro_padre = buffer[posicion_registro_padre]->getCodigo();
		codigo_registro_nuevo = buffer[posicion_registro_nuevo]->getCodigo();

		if (codigo_registro_nuevo < codigo_registro_padre){
			intercambiarRegistros(buffer, posicion_registro_nuevo, posicion_registro_padre);
			posicion_registro_nuevo = posicion_registro_padre;
		} else{
			heap_reestablecido = true;
		}
	}
}

void reestablecerHeapPorInsercionNombre(Registro *buffer[], int posicion_registro_nuevo){
	char* codigo_registro_nuevo;
	char* codigo_registro_padre;
	int posicion_registro_padre;
	bool heap_reestablecido = false;

	while (!heap_reestablecido){
		posicion_registro_padre = (div(posicion_registro_nuevo - 1, 2)).quot;
		codigo_registro_padre = buffer[posicion_registro_padre]->getNombre();
		codigo_registro_nuevo = buffer[posicion_registro_nuevo]->getNombre();

		if (codigo_registro_nuevo < codigo_registro_padre){
			intercambiarRegistros(buffer, posicion_registro_nuevo, posicion_registro_padre);
			posicion_registro_nuevo = posicion_registro_padre;
		} else{
			heap_reestablecido = true;
		}
	}
}


// Inserta un registro en el Buffer, utilizando Montículos como método de ordenamiento.

void insertarRegistroEnBuffer(Registro *buffer[], Registro *registro_auxiliar, int posicion_registro_nuevo,int dato){

	buffer[posicion_registro_nuevo] = registro_auxiliar;

	if (posicion_registro_nuevo > 0){
		if(dato==1) reestablecerHeapPorInsercionCodigo(buffer, posicion_registro_nuevo);
		else reestablecerHeapPorInsercionNombre(buffer, posicion_registro_nuevo);
	}
}

//Reestrablece el Heap, luego de realizar una eliminación.

void reestablecerHeapPorEliminacionDeRaizCodigo(Registro *buffer[], int posicion_ultimo_registro){

	Registro *hijo_izquierdo, *hijo_derecho, *padre;
	int posicion_padre, posicion_hijo_izq, posicion_hijo_der;
	int codigo_padre, codigo_hijo_izq, codigo_hijo_der;
	bool heap_reestablecido = false;

	posicion_padre = 0;

	while (!heap_reestablecido){

		padre = buffer[posicion_padre];
		codigo_padre = padre->getCodigo();


		posicion_hijo_izq = 2*posicion_padre + 1;
		posicion_hijo_der = 2*posicion_padre + 2;

		if (posicion_hijo_izq < posicion_ultimo_registro){
			hijo_izquierdo = buffer[posicion_hijo_izq];
			codigo_hijo_izq = hijo_izquierdo->getCodigo();
		} else{
			codigo_hijo_izq = codigo_padre + 1;
		}

		if (posicion_hijo_der < posicion_ultimo_registro){
			hijo_derecho = buffer[posicion_hijo_der];
			codigo_hijo_der = hijo_derecho->getCodigo();
		} else{
			codigo_hijo_der = codigo_padre + 1;
		}

		if ((codigo_hijo_izq <= codigo_hijo_der) && (codigo_hijo_izq <= codigo_padre)){
			intercambiarRegistros(buffer, posicion_hijo_izq, posicion_padre);
			posicion_padre = posicion_hijo_izq;
		}

		if ((codigo_hijo_der <= codigo_hijo_izq) && (codigo_hijo_der <= codigo_padre)){
			intercambiarRegistros(buffer, posicion_hijo_der, posicion_padre);
			posicion_padre = posicion_hijo_der;
		}

		if ((codigo_padre <= codigo_hijo_izq) && (codigo_padre <= codigo_hijo_der)){
			heap_reestablecido = true;
		}

	}


}


void reestablecerHeapPorEliminacionDeRaizNombre(Registro *buffer[], int posicion_ultimo_registro){

	Registro *hijo_izquierdo, *hijo_derecho, *padre;
	int posicion_padre, posicion_hijo_izq, posicion_hijo_der;
	char *codigo_padre, *codigo_hijo_izq, *codigo_hijo_der;
	bool heap_reestablecido = false;

	posicion_padre = 0;

	while (!heap_reestablecido){

		padre = buffer[posicion_padre];
		codigo_padre = padre->getNombre();

		posicion_hijo_izq = 2*posicion_padre + 1;
		posicion_hijo_der = 2*posicion_padre + 2;

		if (posicion_hijo_izq < posicion_ultimo_registro){
			hijo_izquierdo = buffer[posicion_hijo_izq];
			codigo_hijo_izq = hijo_izquierdo->getNombre();
		} else{
			codigo_hijo_izq = codigo_padre + 1;
		}

		if (posicion_hijo_der < posicion_ultimo_registro){
			hijo_derecho = buffer[posicion_hijo_der];
			codigo_hijo_der = hijo_derecho->getNombre();
		} else{
			codigo_hijo_der = codigo_padre + 1;
		}

		if ((strcmp(codigo_hijo_izq,codigo_hijo_der)<=0) && (strcmp(codigo_hijo_izq,codigo_padre)<=0)){
			intercambiarRegistros(buffer, posicion_hijo_izq, posicion_padre);
			posicion_padre = posicion_hijo_izq;
		}

		if ((strcmp(codigo_hijo_der,codigo_hijo_izq)<=0) && (strcmp(codigo_hijo_der,codigo_padre)<=0)){
			intercambiarRegistros(buffer, posicion_hijo_der, posicion_padre);
			posicion_padre = posicion_hijo_der;
		}

		if ((strcmp(codigo_padre,codigo_hijo_izq)<=0) && (strcmp(codigo_padre,codigo_hijo_der)<=0)){
			heap_reestablecido = true;
		}

	}


}

// Elimina el primer elemento en el Buffer, reemplazándolo por el último registro activo, y reorganizando el mismo
// utilizando Montículos como método de ordenamiento.
Registro eliminarRaiz(Registro *buffer[], int posicion_ultimo_registro,int dato){

	Registro *registro_de_salida = buffer[0];
	if (posicion_ultimo_registro == 0){
		buffer[0] = NULL;
	} else{
		buffer[0] = buffer[posicion_ultimo_registro];
		buffer[posicion_ultimo_registro] = NULL;
		if(dato==1) reestablecerHeapPorEliminacionDeRaizCodigo(buffer, posicion_ultimo_registro);
		else reestablecerHeapPorEliminacionDeRaizNombre(buffer, posicion_ultimo_registro);
	}
	return *registro_de_salida;
}

// "Congela" el registro dentro del buffer.
void congelarRegistro(Registro *buffer[], Registro *puntero_a_congelar, int cantidad_registros_activos){

	if (buffer[cantidad_registros_activos] == NULL){
		buffer[cantidad_registros_activos] = puntero_a_congelar;
	}
}


//Abre un nuevo bloque de un archivo. Pasa por parámetro la cantidad de RR. Devuelve un puntero al primer registro.
Bloque* abrirNuevoBloque(FILE *archivo, int *registros_totales_bloque, char *&posicion_interna_bloque){

	Bloque *puntero_a_bloque = new Bloque();
	fread(puntero_a_bloque->getBloque(), TAMANIO_BLOQUE, 1, archivo);
	posicion_interna_bloque = puntero_a_bloque->getBloque();
	memcpy(registros_totales_bloque, posicion_interna_bloque, 4);
	posicion_interna_bloque += 4;
	return puntero_a_bloque;
}

char* cargaInicial(Registro *buffer[], FILE *archivo, int *registros_totales_bloque, int *registros_bloque_parcial, Bloque *bloque_actual,int dato){

	int registros_en_buffer = 0;
	int limite = PROMEDIO_REGISTROS * 2;
	char *posicion_interna_bloque;
	Registro *registro_auxiliar;

	while((!feof(archivo)) && (registros_en_buffer < limite)){

		bloque_actual = abrirNuevoBloque(archivo, registros_totales_bloque, posicion_interna_bloque);
		(*registros_bloque_parcial) = 0;

		while ((registros_en_buffer < limite) && ((*registros_bloque_parcial) < (*registros_totales_bloque))){

			registro_auxiliar = generarRegistro(posicion_interna_bloque);
			insertarRegistroEnBuffer(buffer, registro_auxiliar, registros_en_buffer,dato);
			registros_en_buffer++;
			(*registros_bloque_parcial)++;
		}
		if ((*registros_bloque_parcial) == (*registros_totales_bloque)){
			posicion_interna_bloque = NULL;
			delete (bloque_actual);
		}
	}
	return posicion_interna_bloque;
}

/*
Modificar para que guarde el bloque, dentro del archivo que se le pasa por parámetro.
void archivarBloque( FILE* archivo, Bloque* puntero_a_bloque){
	fwrite(puntero_a_bloque, TAMANIO_BLOQUE, 1, archivo);
	free(puntero_a_bloque);
}
*/
char* crearNombreArchivoAuxiliar(int numero_archivo_auxiliar,bool conDat){

	int cantDig = contarDigitos(numero_archivo_auxiliar);
	char *indice = new char[cantDig];
	char *nombre = new char[2 + contarDigitos(numero_archivo_auxiliar)];
	sprintf(indice, "%i", numero_archivo_auxiliar);
	strcpy(nombre, "OE");
	if(conDat) nombre= strcat(strcat(nombre,indice),".dat");
	else nombre=strcat(nombre,indice);
	delete []indice;
	return nombre;

}


//Crea un nuevo archivo binario, el cual llevará el nombre "Ordenamiento Externo ##", donde ## será el número
//corriente de archivos creados para mezclar.
FILE* crearNuevoArchivoAuxiliar(int numero_archivo_auxiliar){
	char *nombre = crearNombreArchivoAuxiliar(numero_archivo_auxiliar,true);
	FILE *archivo_auxiliar = fopen(nombre, "wb");
	delete []nombre;
	return archivo_auxiliar;

}

int sortExternParte1(char *argv[],int dato){

	//Creo el buffer[].
	int tamanio_arreglo = (PROMEDIO_REGISTROS * 2);
	Registro *buffer[tamanio_arreglo];

	//Abro el archivo de entrada.
	FILE *archivo_base = abrirArchivoBinario(argv, 2);

	//Creo el primer archivo de trabajo.
	int numero_archivo = 0;
	FILE *archivo_auxiliar = crearNuevoArchivoAuxiliar(numero_archivo);

	//Carga masiva inicial, desde el archivo de entrada al buffer. Ordenamiento por Montículos.
	char *posicion_interna_bloque;
	int registros_totales_del_bloque = 0;
	int registros_cargados_del_bloque_actual = 0;
	Bloque *bloque_actual = NULL;
	posicion_interna_bloque = cargaInicial(buffer, archivo_base, &registros_totales_del_bloque, &registros_cargados_del_bloque_actual, bloque_actual,dato);
	int cantidad_registros_activos = tamanio_arreglo;

	//Creo Bloque y Registro auxiliares.
	Bloque *bloque_auxiliar = new Bloque();
	//Bloque *puntero_bloque_auxiliar = &bloque_auxiliar;
	Registro registro_auxiliar = Registro();
	Registro *puntero_registro_bloque = &registro_auxiliar;
	Registro registro_de_salida;
	int codigo_registro_buffer;
	int codigo_registro_bloque;

	while ((!feof(archivo_base)) || (posicion_interna_bloque != NULL)){


		//Hay que verificar si el próximo registro a leer, se encuentra en un bloque nuevo.
		if (posicion_interna_bloque == NULL){
			delete (bloque_actual);
			bloque_actual = abrirNuevoBloque(archivo_base, &registros_totales_del_bloque, posicion_interna_bloque);
			registros_cargados_del_bloque_actual = 0;
			if(registros_totales_del_bloque==0){
				break;
			}
		}

		//Si la # de RRAA en Buffer es 0 -> Todos están Congelados -> Guardo el bloque, cierro Archivo,
		// y a ordenar el buffebloque_auxiliarr again.
		if (cantidad_registros_activos==0){

			for(int i=0; i<(tamanio_arreglo); i++){
				if(dato==1) reestablecerHeapPorInsercionCodigo(buffer, i);
				else reestablecerHeapPorInsercionNombre(buffer, i);
			}
			//archivarBloque(archivo_auxiliar, puntero_bloque_auxiliar);
			fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_auxiliar);
			fclose(archivo_auxiliar);
			numero_archivo++;
			archivo_auxiliar = crearNuevoArchivoAuxiliar(numero_archivo);
			delete bloque_auxiliar;
			bloque_auxiliar = new Bloque();
			//puntero_bloque_auxiliar = &bloque_auxiliar;
			cantidad_registros_activos = tamanio_arreglo;
		}

		puntero_registro_bloque = generarRegistro(posicion_interna_bloque);
		registros_cargados_del_bloque_actual++;

		if (registros_cargados_del_bloque_actual == registros_totales_del_bloque){
			posicion_interna_bloque = NULL;
		}

		codigo_registro_bloque = puntero_registro_bloque->getCodigo();
		codigo_registro_buffer = (buffer[0])->getCodigo();
		registro_de_salida = eliminarRaiz(buffer, (cantidad_registros_activos-1),dato);

		if (bloque_auxiliar->lleno(&registro_de_salida) ){
			fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_auxiliar);
			delete bloque_auxiliar;
			bloque_auxiliar = new Bloque();

		}


		bloque_auxiliar->agregarRegistro(&registro_de_salida);


		if (codigo_registro_buffer <= codigo_registro_bloque){
			insertarRegistroEnBuffer(buffer, puntero_registro_bloque, (cantidad_registros_activos-1),dato);
		}else{
			congelarRegistro(buffer, puntero_registro_bloque, cantidad_registros_activos-1);
			(cantidad_registros_activos)--;
		}

	}


	//Libero al bloque_actual, ya que todos los registros del archivo han sido cargados.
	delete (bloque_actual);

	//int posicion_primer_registro_congelado = cantidad_registros_activos;

	//Luego de que se termina el archivo se procede:
	// 1º Terminando de escribir los registros activos en el archivo, cerrando este al finalizar.
	// 2º Reordenando el buffer para los registros congelados, y colocandolos en el ultimo archivo de salida.

	for(int posicion_ultimo_activo = (cantidad_registros_activos-1); posicion_ultimo_activo >= 0; posicion_ultimo_activo--){

		registro_de_salida = eliminarRaiz(buffer, posicion_ultimo_activo,dato);

		if (bloque_auxiliar->lleno(&registro_de_salida) ){
			fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_auxiliar);
			delete bloque_auxiliar;
			bloque_auxiliar = new Bloque();
		}
		bloque_auxiliar->agregarRegistro(&registro_de_salida);
		//puntero_bloque_auxiliar->agregarRegistro(registro_de_salida);
	}

	fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_auxiliar);
	fclose(archivo_auxiliar);
	numero_archivo++;
	archivo_auxiliar = crearNuevoArchivoAuxiliar(numero_archivo);
	delete bloque_auxiliar;
	bloque_auxiliar = new Bloque();

	Registro *puntero_registro_auxiliar;
	int posicion_actual = 0;
	for(int posicion_primer_registro_congelado = cantidad_registros_activos; posicion_primer_registro_congelado < (tamanio_arreglo); posicion_primer_registro_congelado++){
		puntero_registro_auxiliar = buffer[posicion_primer_registro_congelado];
		insertarRegistroEnBuffer(buffer, puntero_registro_auxiliar, posicion_actual,dato);
		posicion_actual++;
		buffer[posicion_primer_registro_congelado] = NULL;
	}

	for(int posicion_ultimo_activo = (posicion_actual-1); posicion_ultimo_activo >= 0; posicion_ultimo_activo--){
		registro_de_salida = eliminarRaiz(buffer, posicion_ultimo_activo,dato);

		if (bloque_auxiliar->lleno(&registro_de_salida) ){
			fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_auxiliar);
			delete bloque_auxiliar;
			bloque_auxiliar = new Bloque();
		}
		bloque_auxiliar->agregarRegistro(&registro_de_salida);
		//puntero_bloque_auxiliar->agregarRegistro(registro_de_salida);
	}

	fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivo_auxiliar);
	delete bloque_auxiliar;
	fclose(archivo_auxiliar);
	fclose(archivo_base);
	return numero_archivo;  //AGREGE ESTOOO ANTES ERA VOID

}


//corriente de archivos creados para mezclar.
FILE* abrirNuevoArchivoAuxiliar(int numero_archivo_auxiliar){
	char *nombre = crearNombreArchivoAuxiliar(numero_archivo_auxiliar,true);
	FILE *archivo_auxiliar = fopen(nombre, "rb");
	delete []nombre;
	return archivo_auxiliar;
}

Registro* compararRegistrosCodigo(Registro* r1,int arch1,Registro* r2,int arch2,int *archmin){
	if((r1==NULL) && (r2==NULL))return NULL;
	if(r1!=NULL && r2!=NULL){
		if((r1->getCodigo())< (r2->getCodigo())){
			*archmin=arch1;
			return r1;
		}else{
			*archmin=arch2;
			return r2;
		}
	}else{
		if(r1==NULL){
			*archmin=arch2;
			return r2;
		}
		else{
			*archmin=arch1;
			return r1;
		}

	}
}

Registro* compararRegistrosNombre(Registro* r1,int arch1,Registro* r2,int arch2,int *archmin){
	if((r1==NULL) && (r2==NULL)){
		return NULL;
	}
	if(r1!=NULL && r2!=NULL){
		if(strcmp(r1->getNombre(),r2->getNombre())<0){
			*archmin=arch1;
			return r1;
		}else{
			*archmin=arch2;
			return r2;
		}
	}else{
		if(r1==NULL){
			*archmin=arch2;
			return r2;
		}
		else{
			*archmin=arch1;
			return r1;
		}

	}
}

void ordenarArchivosEnUno(FILE* listaDeArchivos[],int cantArchivosEnLista,int numeroArchAuxiliar,int dato){
	FILE* archivoAuxiliar = crearNuevoArchivoAuxiliar(numeroArchAuxiliar);
	Bloque* listaDeBloques[cantArchivosEnLista];
	Bloque *bloque_auxiliar = new Bloque();
	char* listaDePosInterna[3];
	int listaDeRegistros[3];
	int listaDeRegistrosCargados[]={0,0,0};
	Registro* listaDeRegMinimos[]={NULL,NULL,NULL};;
	Registro* minimo;
	Registro* registro_auxiliar;

	for(int i=0;i<cantArchivosEnLista;i++){
		char* posicion_interna_bloque;
		listaDePosInterna[i]=posicion_interna_bloque;
		int registros_totales_del_bloque = 0;
		listaDeRegistros[i]=registros_totales_del_bloque;

		if((listaDeArchivos[i]) != NULL) {
			Bloque* bloque = abrirNuevoBloque(listaDeArchivos[i],&listaDeRegistros[i],listaDePosInterna[i]);
			listaDeBloques[i]=bloque;

		}

		registro_auxiliar = generarRegistro(listaDePosInterna[i]);
		listaDeRegMinimos[i]=registro_auxiliar;
		listaDeRegistrosCargados[i] ++;

	}

	int archivomin;
	while((!feof(listaDeArchivos[0]))||(!feof(listaDeArchivos[1]))||(!feof(listaDeArchivos[2]))){

		if(dato==1){
			minimo=compararRegistrosCodigo(listaDeRegMinimos[0],0,listaDeRegMinimos[1],1,&archivomin);
			minimo=compararRegistrosCodigo(minimo,archivomin,listaDeRegMinimos[2],2,&archivomin);
		}else{
			minimo=compararRegistrosNombre(listaDeRegMinimos[0],0,listaDeRegMinimos[1],1,&archivomin);
			minimo=compararRegistrosNombre(minimo,archivomin,listaDeRegMinimos[2],2,&archivomin);
		}
		if(minimo ==NULL){
			fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivoAuxiliar);
			delete bloque_auxiliar;
			for(int i=0;i<cantArchivosEnLista;i++){
				delete listaDeBloques[i];
				fclose(listaDeArchivos[i]);
			}
			fclose(archivoAuxiliar);
			return;
		}
		if (bloque_auxiliar->lleno(minimo)){
			fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivoAuxiliar);
			delete bloque_auxiliar;
			bloque_auxiliar = new Bloque();

		}
		bloque_auxiliar->agregarRegistro(minimo);

		if (listaDePosInterna[archivomin] == NULL){
			delete (listaDeBloques[archivomin]);
			listaDeBloques[archivomin] = abrirNuevoBloque(listaDeArchivos[archivomin],&listaDeRegistros[archivomin],listaDePosInterna[archivomin]);
			listaDeRegistrosCargados[archivomin] = 0;
			if(listaDeRegistros[archivomin]==0){
				listaDeRegMinimos[archivomin]=NULL;
			}
		}
		if(listaDeRegMinimos[archivomin]!=NULL){
			registro_auxiliar = generarRegistro(listaDePosInterna[archivomin]);
			listaDeRegMinimos[archivomin]=registro_auxiliar;
			listaDeRegistrosCargados[archivomin] ++;


			if (listaDeRegistrosCargados[archivomin] == listaDeRegistros[archivomin]){
				listaDePosInterna[archivomin] = NULL;
			}
		}
	}

	for(int i=0;i<cantArchivosEnLista;i++){
		delete listaDeBloques[i];
		fclose(listaDeArchivos[i]);
	}
	fwrite(bloque_auxiliar->getBloque(), TAMANIO_BLOQUE, 1, archivoAuxiliar);
	delete bloque_auxiliar;
	fclose(archivoAuxiliar);
	return;


}

int ordenarArchivos(int cantArchivos,int numeroArchivo,int numeroArchAuxiliar,int dato){

	if(cantArchivos==1){
		return numeroArchivo;
	}
	int cantArchivosOrdenados = 0;
	while(cantArchivos!= 0){
		FILE* listadoDeArchivos[3];
		int cantArchivosEnLista = 0;

		for(int i=0;i<3;i++){
			if(cantArchivos != 0){
				FILE* archivo = abrirNuevoArchivoAuxiliar(numeroArchivo);
				numeroArchivo ++;
				cantArchivos--;
				if(archivo !=NULL){
					listadoDeArchivos[i]=archivo;
					cantArchivosEnLista++;
				}
			}
		}
		ordenarArchivosEnUno(listadoDeArchivos,cantArchivosEnLista,numeroArchAuxiliar,dato);
		numeroArchAuxiliar++;
		cantArchivosOrdenados ++;
	}
	numeroArchivo = ordenarArchivos(cantArchivosOrdenados,numeroArchivo,numeroArchAuxiliar,dato);
	return numeroArchivo;
}

void sortExtern(char *argv[],int dato){

	int cantArchivos= sortExternParte1(argv,dato);
	int numeroArchivo=0;
	numeroArchivo = ordenarArchivos(cantArchivos+1,numeroArchivo,cantArchivos+1,dato);
	char *nombre = crearNombreArchivoAuxiliar(numeroArchivo,false);

	char* vector[3];
	vector[2]=nombre;
	listadoDeEntidades(vector);



}


//ACÁ SIGUE LO QUE NO HAY QUE DEBUGEAR

//Se deriva a la función correspondiente por el comando ingresado.
//Si error = 0, no hubo errores.
//Si error = 1, el comando ingresado es incorrecto.
//Si error = 2, la cantidad de parámetros ingresada, es incorrecta.
void tratarError(int error){
	switch(error){
		case 1:{
			cout << "El comando ingresado es incorrecto." << endl;
			break;
		}
		case 2:{
			cout << "La cantidad de parámetros ingresada es incorrecta." << endl;
			break;
		}
		default:
			break;
	}
}


int definirComando(int argc, char* argv[]) {
	int operador = 0;
	int error = 0;
	if(argc > 2){
		if(!strcmp(argv[1], carga))
			operador = 1;
		if(!strcmp(argv[1], listado))
			operador = 2;
		if(!strcmp(argv[1], clave))
			operador = 3;
		if(!strcmp(argv[1], ordClave))
			operador = 4;
		if(!strcmp(argv[1], ordNombre))
			operador = 5;
		switch(operador){
			case 1:{
				if(argc > 2){
					cargarEntidades(argv);
				} else{
					error = 2;
				}
				break;
			}
			case 2:{
				listadoDeEntidades(argv);
				break;
			}
			case 3:{
				if(argc > 2){
					buscarPorClave(argv);
				} else{
					error = 2;
				}
				break;
			}
			case 4:{
				cout << "Listado ordenado por clave:" << endl; //Colocar esta linea dentro de la funcion correspondiente
				sortExtern(argv,1);
				break;
			}
			case 5:{
				cout << "Listado ordenado por nombre:" << endl; //Colocar esta linea dentro de la funcion correspondiente
				sortExtern(argv,0);
				break;
			}
			default:{
				error = 1;
				break;
			}
		}
	} else{
		error = 2;
	}
	return error;
}


int main(int argc, char *argv[]){

	int error = definirComando(argc, argv);
	tratarError(error);

	return 0;


	//listadoDeEntidades(argv);
	//sortExtern(argv);

}

//Acá están las instrucciones para probarlas. En el primer caso, cambien el directorio.

//./cargador_masivo -i animales /home/facundo/Escritorio
//./cargador_masivo -l animales
//./cargador_masivo -k 100 animales
//./cargador_masivo -ok animales
//./cargador_masivo -on animales
